package com.eleadin.ruleengine.easyrules.utils;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.eleadin.ruleengine.easyrules.constants.EasyRulesConstants;
import com.eleadin.ruleengine.easyrules.enums.EasyRulesOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.springframework.util.ObjectUtils;

@Slf4j
@Data
public class EasyRulesUtil {

    private boolean isJson;

    public EasyRulesUtil() {

    }

    public EasyRulesUtil(boolean isJson) {
        this.isJson = isJson;
    }

    /**
     * 执行规则匹配
     * @param fact 事实json
     * @param ruleModel 规则模型
     */
    public RuleResult match(JSONObject fact, RuleModel ruleModel){
        // 结果
        RuleResult result = new RuleResult();
        result.setRuleId(ruleModel.getRuleId());
        // 规则实例
        Facts facts = new Facts();
        facts.put(EasyRulesConstants.FACT_ALIAS, fact);
        facts.put(EasyRulesConstants.RESULT_ALIAS, result);
        // 规则内容
        org.jeasy.rules.api.Rule mvelRule = new org.jeasy.rules.mvel.MVELRule()
                .name(ruleModel.getRuleName())
                .description(ruleModel.getDescription())
                .when(ruleModel.getWhenExpression())
                .then(ruleModel.getThenExpression());
        // 规则集合
        Rules rules = new Rules();
        // 将规则添加到集合
        rules.register(mvelRule);
        // 创建规则执行引擎，并执行规则
        RulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.fire(rules, facts);
        return result;
    }

    public RuleResult matchObject(Object fact, RuleModel ruleModel){
        // 结果
        RuleResult result = new RuleResult();
        result.setRuleId(ruleModel.getRuleId());
        // 规则实例
        Facts facts = new Facts();
        facts.put(EasyRulesConstants.FACT_ALIAS, fact);
        facts.put(EasyRulesConstants.RESULT_ALIAS, result);
        // 规则内容
        org.jeasy.rules.api.Rule mvelRule = new org.jeasy.rules.mvel.MVELRule()
                .name(ruleModel.getRuleName())
                .description(ruleModel.getDescription())
                .when(ruleModel.getWhenExpression())
                .then(ruleModel.getThenExpression());
        // 规则集合
        Rules rules = new Rules();
        // 将规则添加到集合
        rules.register(mvelRule);
        // 创建规则执行引擎，并执行规则
        RulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.fire(rules, facts);
        return result;
    }

    /**
     * 构建mvel条件表达式
     * @param json 节点json，例如：
     * {
     *     "type": "EXPRESSION",
     *     "operator": "LESS_THAN",
     *     "metricName": "NORMAL_NUMBER",
     *     "value": "11",
     *     "children": []
     * }
     */
    public String buildWhenExpression(JSONObject json) {
        StringBuffer mvelExpression = new StringBuffer();
        String type = json.getString(EasyRulesConstants.FIELD_TYPE);
        String operator = json.getString(EasyRulesConstants.FIELD_OPERATOR);

        switch (type) {
            case EasyRulesConstants.EXPRESSION_TYPE:
                String fieldName = json.getString(EasyRulesConstants.FIELD_NAME);
                String fieldValue = json.getString(EasyRulesConstants.FIELD_VALUE);
                mvelExpression.append(buildOperatorExpress(operator, fieldName, fieldValue));
                break;
            case EasyRulesConstants.RELATION_TYPE:
                JSONArray children = json.getJSONArray(EasyRulesConstants.FIELD_CHILDREN);
                if (children.size() == 0) {
                    log.error("rule is relation type, but has no child node");
                    return EasyRulesConstants.SYMBOL_EMPTY;
                }
                operator = convertRelationExpress(operator);
                StringBuffer childrenExpression = new StringBuffer();
                for (int i = 0; i < children.size(); i++) {
                    JSONObject child = children.getJSONObject(i);
                    // 递归构建单个规则条件
                    String childExpression = buildWhenExpression(child);
                    if (!childExpression.isEmpty()) {
                        if (childrenExpression.length() > 0) {
                            childrenExpression.append(EasyRulesConstants.SYMBOL_SPACE).append(operator).append(EasyRulesConstants.SYMBOL_SPACE);
                        }
                        childrenExpression.append(EasyRulesConstants.LEFT_BRACKETS).append(childExpression).append(EasyRulesConstants.RIGHT_BRACKETS);
                    }
                }
                mvelExpression.append(childrenExpression);
                break;
            default:
                break;
        }
        return mvelExpression.toString();
    }

    /**
     * 构建mvel结果表达式
     */
    public static String buildThenExpression() {
        StringBuffer expression = new StringBuffer();
        expression.append(EasyRulesConstants.RESULT_ALIAS).append(".setValue(\"").append(EasyRulesConstants.MATCH_SUCCESS_MESSAGE).append("\");");
        log.info("thenExpression: ", expression);
        return expression.toString();
    }

    /**
     * 转换条件连接符
     * @param relation 条件连接符
     */
    private static String convertRelationExpress(String relation) {
        if (StringUtils.isEmpty(relation)){
            return EasyRulesConstants.SYMBOL_EMPTY;
        } else if(relation.equalsIgnoreCase(EasyRulesConstants.RELATION_AND)){
            return EasyRulesConstants.LOGICAL_AND;
        } else if(relation.equalsIgnoreCase(EasyRulesConstants.RELATION_OR)){
            return EasyRulesConstants.LOGICAL_OR;
        }
        return relation;
    }

    /**
     * 构建mvel表达式
     * @param operator 操作符
     * @param fieldName 字段名称
     * @param value 字段值
     */
    private String buildOperatorExpress(String operator, String fieldName, Object value) {
        EasyRulesOperation operation = EasyRulesOperation.getOperationByOperator(operator);
        if (!ObjectUtils.isEmpty(operation)) {
            String expression = operation.getExpression();
            return String.format(expression, buildValueExpress(fieldName), value);
        }
        return EasyRulesConstants.SYMBOL_EMPTY;
    }

    /**
     * 构建mvel取值表达式
     * @param fieldName 字段名称
     */
    private String buildValueExpress(String fieldName) {
//        return String.format("%s.get(\"%s\")", EasyRulesConstants.FACT_ALIAS, fieldName);

        // 支持jsonObject对象的属性操作
        // eg: field为 user.name， 则结果为fact.get("user").get("name")
        String[] subStr = fieldName.split("\\.");
        StringBuffer buffer = new StringBuffer();

        if (this.isJson) {
            for (int i = 0; i < subStr.length; ++i) {
                if (i == 0) {
                    buffer.append(String.format("%s.get(\"%s\")", EasyRulesConstants.FACT_ALIAS, subStr[i]));
                } else {
                    buffer.append(String.format(".get(\"%s\")", subStr[i]));
                }
            }
        } else {
            for (int i = 0; i < subStr.length; ++i) {
                if (i == 0) {
                    buffer.append(String.format("%s.%s", EasyRulesConstants.FACT_ALIAS, subStr[i]));
                } else {
                    buffer.append(String.format(".%s", subStr[i]));
                }
            }
        }



        return buffer.toString();
    }

    @Data
    public static class RuleModel {
        private String ruleId;
        private String ruleName;
        private String description;
        private String whenExpression;
        private String thenExpression;
    }

    @Data
    public static class RuleResult {
        // 规则主键
        private String ruleId;

        // 是否匹配, 默认false
        private boolean isMatch = false;

        // 匹配信息，默认为匹配失败
        private String message = "匹配失败";

        /**
         * 匹配成功后设置成功信息
         */
        public void setValue(String message){
            this.message = message;
            this.isMatch = true;
        }
    }
}
